/*
 * ===========================================================================
 *
 *      Filename: buffer.c
 *   Description: dynamic buffer
 *        Author: Ye wenhao, wenhaoye@126.com
 *  Organization: 
 *
 * ===========================================================================
 */
#include "buffer.h"
#include "io_tools.h"

#define BUFFER_DEFAULT_UNIT_BITS 8
#define BUFFER_DEFAULT_SIZE  256

static inline size_t size_adjust(size_t size)
{
    return (((size + BUFFER_DEFAULT_SIZE - 1) >> BUFFER_DEFAULT_UNIT_BITS) << BUFFER_DEFAULT_UNIT_BITS);
}

static void buffer_adjust(buffer_t *self)
{
    assert(self->misalign <= self->off);
    if(self->misalign == 0)
        return;

    if(self->misalign < self->off)
        memmove(self->orig_buf, self->orig_buf + self->misalign, buffer_len(self));
    
    self->off -= self->misalign;
    self->misalign = 0;
}

int buffer_init(buffer_t *self, size_t size)
{
	/* adjust size */
    if(size < BUFFER_DEFAULT_SIZE)
        size = BUFFER_DEFAULT_SIZE;

    if(size % BUFFER_DEFAULT_SIZE){ // align to 256
        size = (((size + BUFFER_DEFAULT_SIZE - 1) >> BUFFER_DEFAULT_UNIT_BITS) << BUFFER_DEFAULT_UNIT_BITS);
    }

    MEMSET_P(self);
    self->min_size = size;
    self->orig_buf = (uint8_t *)MALLOC(size);
    if(self->orig_buf == NULL){
        ERR("malloc fail");
        return -1;
    }

    self->size = size;
    return 0;
}

void buffer_term(buffer_t *self)
{
    safe_free(self->orig_buf);
	MEMSET_P(self);
}

buffer_t *buffer_new(size_t size)
{
    buffer_t *self = (buffer_t *)MALLOC(sizeof(buffer_t));
    if(self){
        if(buffer_init(self, size) < 0)
            buffer_destroy(&self);
    }

    return self;
}

void buffer_destroy(buffer_t **p_self)
{
    if(p_self && *p_self){
        buffer_term(*p_self);
        safe_free(*p_self);
    }
}


int buffer_expand(buffer_t *self, size_t len)
{    
    if((self->size - self->off) >= len)
        return 0;

    if((self->size - self->off + self->misalign) >= len){
        buffer_adjust(self);
        return 0;
    }

    buffer_adjust(self);
    //size_t new_size = self->size + (((len + BUFFER_DEFAULT_SIZE - 1) >> BUFFER_DEFAULT_UNIT_BITS) << BUFFER_DEFAULT_UNIT_BITS);
    size_t new_size = self->size + size_adjust(len);
    uint8_t *p = (uint8_t *)realloc(self->orig_buf, new_size);
    if(p == NULL){
        ERR("realloc fail");
        return -1;
    }

    self->orig_buf = p;
    self->size = new_size;
    return 0;
}

static int buffer_really_drain(buffer_t *self)
{
    if(self->size <= self->min_size)
        return 0;

    size_t t_size = self->size >> 2;
    if(buffer_len(self) > t_size)
        return 0;

    buffer_adjust(self);

    //size_t size = (self->size >> 1);//buffer_len(self) >> 1;
    size_t size = ((((buffer_len(self) >> 1) + BUFFER_DEFAULT_SIZE) >> BUFFER_DEFAULT_UNIT_BITS) << BUFFER_DEFAULT_UNIT_BITS);
    size = MAX(size, self->min_size);
    uint8_t *p = (uint8_t *)realloc(self->orig_buf, size);
    if(p == NULL){
        ERR("realloc fail");
        assert(0);
        return -1;
    }

    self->orig_buf = p;
    self->size = size;
    return 0;
}

int buffer_drain(buffer_t *self, size_t len)
{
    self->misalign += len;
    buffer_really_drain(self);
    return 0;
}

int buffer_add(buffer_t *self, void *data, size_t len)
{
    if(self == NULL || data == NULL){
        DBG("invalid args");
        return -1;
    }

    if(len == 0)
        return 0;

    if(buffer_expand(self, len) < 0){
        return -1;
    }

    memcpy(self->orig_buf + self->off, data, len);
    self->off += len;
    return 0;
}

int buffer_add_buffer(buffer_t *self, buffer_t *buf)
{
    if(self == NULL || buf == NULL){
        DBG("invalid args");
        return -1;
    }

    size_t len = buffer_len(buf);
    if(len == 0)
        return 0;
    
    if(buffer_add(self, buffer_data(buf), len) < 0){
        DBG("buffer_add fail");
        return -1;
    }

    buffer_drain(buf, len);
    return len;
}

int buffer_snprintf(buffer_t *self, const char *fmt, ...)
{
    if(self == NULL || fmt == NULL){
        DBG("NULL args");
        return -1;
    }

    char tmp_buf[1024 * 2];
    int ret = -1;
    va_list ap;
    va_start(ap, fmt);
    ret = vsnprintf(tmp_buf, sizeof(tmp_buf), fmt, ap);
    if(ret <= 0){
        ERR("vsnprintf error");
        return -1;
    }

    if(ret <= sizeof(tmp_buf))
        return buffer_add(self, tmp_buf, ret);

    if(buffer_expand(self, ret) < 0)
        return -1;

    char *space = (char *)buffer_space(self);
    va_start(ap, fmt);
    ret = vsprintf(space, fmt, ap);
    buffer_off_inc(self, ret);
    return 0;
}

int buffer_get(buffer_t *self, void *data, size_t size)
{
    if(buffer_len(self) == 0)
        return 0;

    size = MIN(size, buffer_len(self));
    memcpy(data, self->orig_buf + self->misalign, size);
    buffer_drain(self, size);
    return size;
}

uint8_t *buffer_getline(buffer_t *self, size_t *got_len)
{
    if(self == NULL){
        DBG("NULL point");
        return NULL;
    }

    if(buffer_len(self) == 0)
        return NULL;

    size_t pos = self->misalign;
    int flag = 0;
    while(pos < self->off){
        if(self->orig_buf[pos] == '\r' || self->orig_buf[pos] == '\n'){
            flag = 1;
            break;
        }
        ++pos;
    }

    if(flag == 0)
        return NULL;

    size_t len = pos - self->misalign;
    uint8_t *buf = (uint8_t *)MALLOC(len+1);
    if(buf == NULL){
        ERR("MALLOC FAIL");
        return NULL;
    }

    if(got_len)
        *got_len = len;

    memcpy(buf, self->orig_buf+self->misalign, len);
    if(((pos+1) < self->off) && self->orig_buf[pos+1] == '\n')
        len += 2;
    else
        len += 1;

    buffer_drain(self, len);
    return buf;
}

int buffer_clean(buffer_t *self)
{
    if(self == NULL)
        return -1;

    self->off = 0;
    self->misalign = 0;
    return 0;
}

int buffer_reset(buffer_t *self)
{
    if(!self)
        return -1;

    if(self->size > self->min_size){
        void *ptr = realloc(self->orig_buf, self->min_size);
        if(!ptr){
            ERR("realloc fail");
            assert(0);
            return -1;
        }   

        self->orig_buf = ptr;
        self->size = self->min_size;
    }

    self->misalign = 0;
    self->off = 0;
    return 0;
}

int buffer_from_fd(buffer_t *self, int fd, size_t size)
{
    if(self == NULL || fd < 0 || size == 0){
        DBG("invalid args");
        return -1;
    }
    
    if(buffer_expand(self, size) < 0){
        ERR("buffer_expand fail");
        return -1;
    }

    uint8_t *space = buffer_space(self);
    int ret = fd_read(fd, space, size);
    if(ret < 0){
        ERR("fd_read fail");
        return ret;
    }

    buffer_off_inc(self, ret);
    return ret;
}

int buffer_to_fd(buffer_t *self, int fd, size_t size)
{
    if(self == NULL || fd < 0 || size == 0){
        DBG("invalid args");
        return -1;
    }

    size_t len = buffer_len(self);
    if(len == 0)
        return 0;

    size = MIN(size, len);
    uint8_t *data = buffer_data(self);
    int ret = fd_write(fd, data, size);
    if(ret < 0){
        ERR("fd_write fail");
        return ret;
    }

    if(ret > 0)
        buffer_drain(self, ret);
    return ret;
}
